package com.ayq.activiti.manager.service.impl;

import com.ayq.activiti.common.exception.BusinessException;
import com.ayq.activiti.common.util.ConstantPool;
import com.ayq.activiti.common.util.GlobalHttpSessionUtils;
import com.ayq.activiti.common.util.PageParams;
import com.ayq.activiti.common.util.Ret;
import com.ayq.activiti.manager.config.ActivitiIdGenerator;
import com.ayq.activiti.manager.domain.activitimanager.entity.ActivitiManager;
import com.ayq.activiti.manager.domain.activitimanager.repository.ActivitiManagerMapper;
import com.ayq.activiti.manager.domain.custom.entity.DisDeployment;
import com.ayq.activiti.manager.domain.custom.repository.CustomActivitiMapper;
import com.ayq.activiti.manager.domain.examinedistribution.entity.ActivitiExamineDistribution;
import com.ayq.activiti.manager.domain.examinedistribution.repository.ActivitiExamineDistributionMapper;
import com.ayq.activiti.manager.dto.DeploymentDTO;
import com.ayq.activiti.manager.service.CustomDeploymentService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * <p>部署流程实例接口</p>
 * <p>方法名:</p>
 *
 * @Author ayq
 * @Date 2021/3/25 10:57
 */
@Service
public class CustomDeploymentServiceImpl implements CustomDeploymentService {

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ActivitiIdGenerator activitiIdGenerator;
    @Autowired
    private CustomActivitiMapper customActivitiMapper;
    @Autowired
    private ActivitiManagerMapper activitiManagerMapper;
    @Autowired
    private ActivitiExamineDistributionMapper activitiExamineDistributionMapper;
    @Autowired
    private GlobalHttpSessionUtils globalHttpSessionUtils;

    @Value("${zip-path}")
    private String zipPath;

    public void setZipPath(String zipPath) {
        this.zipPath = zipPath;
    }

    /**
     * <p>删除部署流程实例</p>
     * <p>方法名:deleteDeployment</p>
     *
     * @Author ayq
     * @Date 2021/3/25 10:59
     * @Param [id]
     * @Return void
     */
    @Override
    public void deleteDeployment(String id) throws Exception {
        repositoryService.deleteDeployment(id);
        deleteActivitiManager(id);
    }


    /**
     * <p>强制删除部署流程</p>
     * <p>方法名:deleteRmDeployment</p>
     *
     * @Author ayq
     * @Date 2021/3/25 11:32
     * @Param [id]
     * @Return void
     */
    @Override
    @Transactional
    public void deleteRmDeployment(String id) throws Exception {
        repositoryService.deleteDeployment(id, true);
        deleteActivitiManager(id);

    }

    /**
     *
     * <p>级联删除相关流程信息</p>
     * <p>方法名:deleteActivitiManager</p>
     * @Author ayq
     * @Date 2021/4/1 14:07
     * @Param [id]
     * @Return void
     */
    private void deleteActivitiManager(String id) {
        QueryWrapper<ActivitiManager> wrapper = new QueryWrapper<ActivitiManager>();
        wrapper.eq(ConstantPool.DEPLOYMENT_ID,id);
        List<ActivitiManager> activitiManagers = activitiManagerMapper.selectList(wrapper);
        for (ActivitiManager activitiManager : activitiManagers) {
            QueryWrapper<ActivitiExamineDistribution> wrapper1 = new QueryWrapper<ActivitiExamineDistribution>();
            wrapper1.eq(ConstantPool.ACTIVITI_MANAGER_ID,activitiManager.getId());
            activitiExamineDistributionMapper.delete(wrapper1);
        }
        QueryWrapper<ActivitiManager> wrapper2 = new QueryWrapper<ActivitiManager>();
        wrapper.eq(ConstantPool.DEPLOYMENT_ID,id);
        activitiManagerMapper.delete(wrapper2);
    }

    /**
     * <p>获取流程列表</p>
     * <p>方法名:deploymentList</p>
     *
     * @Author ayq
     * @Date 2021/3/25 11:03
     * @Param [pageParams]
     * @Return void
     */
    @Override
    public void deploymentList(PageParams pageParams) {
        List<DisDeployment> list = customActivitiMapper.getDeployment(((pageParams.getPage()) * pageParams.getLimit()), pageParams.getLimit());
        pageParams.setRecords(list);
    }

    /**
     * <p>部署流程实例</p>
     * <p>方法名:deploy</p>
     *
     * @Author ayq
     * @Date 2021/3/25 11:14
     * @Param [id, zos, zis]
     * @Return void
     */
    @Override
    public void deploy(String id, ZipOutputStream zos, ZipInputStream zis) throws Exception {
        //获取模型
        Model modelData = repositoryService.getModel(id);
        byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
        if (bytes == null) {
            throw new BusinessException("模型数据为空，请先设计流程并成功保存，再进行发布。");
        }
        JsonNode modelNode = new ObjectMapper().readTree(bytes);

        BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
        if (model.getProcesses().size() == 0) {
            throw new BusinessException("数据模型不符要求，请至少设计一条主线流程。");
        }
        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
        //发布流程
        String nextId = activitiIdGenerator.getNextId();
        String processName = nextId + ".bpmn";
        byte[] pngBytes = repositoryService.getModelEditorSourceExtra(modelData.getId());
        String pngName = nextId + ".png";
        String zipPathAll = zipPath + nextId + ".zip";
        Map<String, byte[]> map = new HashMap<>();
        map.put(pngName, pngBytes);
        map.put(processName, bpmnBytes);
        Set<String> strings = map.keySet();
        zos = new ZipOutputStream(new FileOutputStream(zipPathAll));
        for (String string : strings) {
            byte[] zipBytes = map.get(string);
            zos.putNextEntry(new ZipEntry(string));
            zos.write(map.get(string), 0, zipBytes.length);
        }
        zos.finish();
        zis = new ZipInputStream(new FileInputStream(zipPathAll), Charset.forName("GBK"));
        final DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        Deployment deployment = deploymentBuilder.name(modelData.getName())
                .key(modelData.getKey())
                .addZipInputStream(zis)
                .tenantId(globalHttpSessionUtils.getCurrentUserId())
                .deploy();
    }

    /**
     * <p>根据部署流程ID查看模型流程</p>
     * <p>方法名:findModelToDid</p>
     *
     * @Author ayq
     * @Date 2021/3/25 11:19
     * @Param [deploymentId]
     * @Return org.activiti.engine.repository.Model
     */
    @Override
    public Model findModelToDid(String deploymentId) {
        Model model = repositoryService.createModelQuery().deploymentId(deploymentId).singleResult();
        return model;
    }

    /**
     * <p>根据部署ID查看流程图</p>
     * <p>方法名:getDeploymentIdBpmnImgs</p>
     *
     * @Author ayq
     * @Date 2021/3/25 11:24
     * @Param [deploymentId, resourceName, resp]
     * @Return void
     */
    @Override
    public void getDeploymentIdBpmnImgs(String deploymentId, String resourceName, HttpServletResponse resp) throws Exception {
        ServletOutputStream outputStream = resp.getOutputStream();
        InputStream imageStream = repositoryService.getResourceAsStream(deploymentId, resourceName);
        // 输出图片内容
        outputStream = resp.getOutputStream();
        BufferedImage read = ImageIO.read(imageStream);
        ImageIO.write(read, "png", outputStream);
    }

    /**
     * <p>根据部署ID查看流程信息</p>
     * <p>方法名:getDeploymentIdBpmnXml</p>
     *
     * @Author ayq
     * @Date 2021/3/25 14:26
     * @Param [deploymentId, resourceName]
     * @Return java.lang.String
     */
    @Override
    public String getDeploymentIdBpmnXml(String deploymentId, String resourceName) throws Exception {
        InputStream xmlStream = repositoryService.getResourceAsStream(deploymentId, resourceName);
        return IOUtils.toString(xmlStream, "utf-8");
    }

    /**
     * <p>设置部署流程实例状态</p>
     * <p>方法名:setSuspended</p>
     *
     * @Author ayq
     * @Date 2021/3/25 17:35
     * @Param [key, status]
     * @Return boolean
     */
    @Override
    public boolean setSuspended(String key, boolean status) {
        //3.查询流程定义的对象
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(key).singleResult();
        //4.得到当前流程定义的实例是否都为暂停状态
        boolean suspended = processDefinition.isSuspended();
        String processDefinitionId = processDefinition.getId();
        if (status == suspended) {
            //5.判断
            if (suspended) {
                //说明是暂停，就可以激活操作
                repositoryService.activateProcessDefinitionById(processDefinitionId, true
                        , null);
            } else {
                repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null);
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * <p>获取流程所有版本</p>
     * <p>方法名:deploymentAllListVersion</p>
     *
     * @Author ayq
     * @Date 2021/3/26 15:45
     * @Param [pageParams, key]
     * @Return void
     */
    @Override
    public void deploymentAllListVersion(PageParams pageParams, String key) {
        List<Deployment> deployments = repositoryService.createDeploymentQuery().processDefinitionKey(key).orderByDeploymenTime().desc().listPage(pageParams.getPage(), pageParams.getLimit());
        List<DeploymentDTO> list = new ArrayList<>();
        if (deployments != null || deployments.size() != 0) {
            for (Deployment deployment : deployments) {
                ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
                DeploymentDTO deploymentDTO = new DeploymentDTO(deployment, processDefinition);
                list.add(deploymentDTO);
            }
        }
        pageParams.setRecords(list);
    }

    /**
     * <p>模型文件下载</p>
     * <p>方法名:exportActivitiZip</p>
     *
     * @Author ayq
     * @Date 2021/3/26 17:30
     * @Param [resourceName, res, bis, os]
     * @Return void
     */
    @Override
    public void exportActivitiZip(String resourceName, HttpServletResponse res, BufferedInputStream bis, OutputStream os) throws Exception {
        String name = resourceName.substring(0, resourceName.lastIndexOf(".")) + ".zip";
        res.setHeader("content-type", "application/octet-stream");
        res.setContentType("application/octet-stream");
        res.setHeader("Content-Disposition", "attachment; filename=" + name);
        byte[] buff = new byte[1024];
        os = res.getOutputStream();
        bis = new BufferedInputStream(new FileInputStream(
                new File(zipPath + name)));
        int i = bis.read(buff);
        while (i != -1) {
            os.write(buff, 0, buff.length);
            os.flush();
            i = bis.read(buff);
        }
    }

    /**
     * <p>获取部署列表</p>
     * <p>方法名:deploymentAllList</p>
     *
     * @Author ayq
     * @Date 2021/3/30 17:50
     * @Param []
     * @Return java.util.List<org.ezca.esign.service.activiti.domain.custom.entity.DisDeployment>
     */
    @Override
    public List<DisDeployment> deploymentAllList() {
        return customActivitiMapper.getDeploymentAll();
    }

    /**
     * <p>根据流程key获取流程版本</p>
     * <p>方法名:getVersionByKey</p>
     *
     * @Author ayq
     * @Date 2021/3/31 11:46
     * @Param [key]
     * @Return java.util.List<java.lang.Integer>
     */
    @Override
    public List<Integer> getVersionByKey(String key) {
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).list();
        List<Integer> listVersion = new ArrayList<>();
        for (ProcessDefinition processDefinition : list) {
            listVersion.add(processDefinition.getVersion());
        }
        return listVersion;
    }
}
